package com.fin.zw.aiqas.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.fin.zw.aiqas.entity.*;
import com.fin.zw.aiqas.enums.*;
import com.fin.zw.aiqas.model.component.Drill;
import com.fin.zw.aiqas.model.component.JsonDataModel;
import com.fin.zw.aiqas.model.request.CqTaskRequest;
import com.fin.zw.aiqas.model.response.ResultResponse;
import com.fin.zw.aiqas.repository.*;
import com.fin.zw.aiqas.util.*;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.ListUtils;
import org.thymeleaf.util.SetUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service("qcTaskInfoService")
public class QcTaskInfoService {

    private final static Logger log = LoggerFactory.getLogger(QcTaskInfoService.class);

    @Autowired
    private InspectionDetailRepository inspectionDetailRepository;

    @Autowired
    private InspectionRepository inspectionRepository;

    @Autowired
    private ConverseRepository converseRepository;

    @Autowired
    private CalculationResultService calculationResultService;

    @Autowired
    private TaskInfoRepository taskInfoRepository;

    @Autowired
    private ScenesRepository scenesRepository;

    @Autowired
    private TemplateInfoService templateInfoService;

    @Autowired
    private ConverseRuleRepository converseRuleRepository;

    @Autowired//质检点表操作
    private FormulaInfoRepository formulaInfoRepository;

    @Autowired
    private FileTransService fileTransService;

    @Value("${root.drill}")
    private String drillUrl;

    @Value("${speech.recognition.service}")
    private String speechRecognitionServer;

    /**
     * 任务的进行翻译和关键词的质检
     */

    public void executeTaskInfo(List<InspectionDetail> details, Long taskId) {
        for (InspectionDetail del : details) {
            Optional<TaskInfo> optional = taskInfoRepository.findById(taskId);
            if (!optional.isPresent()) {
                throw new BadRequestException("", "推送的任务信息不存在！+");
            }
            TaskInfo taskInfo = optional.get();
            //判断在质检过程中任务是否被暂停
            if (taskInfo.getStatus().equals(Status.START)) {
                String result = new String();
                String status = "success";
                try {
                    if ("aliyun".equals(speechRecognitionServer)) {
                        String jsonData = fileTransService.speechRecognition(del.getVoiceUrl());
                        result = fileTransService.formatResult(jsonData);
                    } else if ("xunfei".equals(speechRecognitionServer)) {
                        result = AudioUtil.audioToText(del.getVoiceUrl());
                    } else {
                        log.info("未知的语音识别服务标识,请联系管理员!!!");
                        throw new BadRequestException("", "未知的语音识别服务标识,请联系管理员!!!");
                    }
                } catch (Exception e) {
                    status = "fail";
                    log.error("文件路径：" + del.getVoiceUrl() + "翻译出错了！");
                } finally {
                    qcFailDetail(del, status);
                    if (status.equals("success")) {
                        qcDoTaskInfo(del, result);
                    }
                }
            }
        }
    }

    public void qcFailDetail(InspectionDetail info, String status) {
        log.info("文件解析失败！");
        info.setDetailStatus(status);
        if (status.equals("fail")) {
            info.setQualityStatus(QualityStatus.FINISH);
        } else {
            info.setQualityStatus(QualityStatus.QUALITYING);
        }
        inspectionDetailRepository.save(info);
        InspectionInfo inspectionInfo = info.getInspectionInfo();
        //保存文件中命中的所有规则
        inspectionInfo.setTaskStatus(InspTask.EXCUTING);
        inspectionInfo.setComplateNum(inspectionInfo.getComplateNum() + 1);
        inspectionRepository.save(inspectionInfo);
        updateTaskInfoByInspectionDetail(inspectionInfo);
    }

    /**
     * 对任务进行质检
     *
     * @param info
     * @param jsonData
     */
    @Transactional
    public void qcDoTaskInfo(InspectionDetail info, String jsonData) {
        log.info("修改质检明细状态：" + info.getId());
        Set<RuleInfo> ruleInfos = new HashSet<>();//获取文件中所有的规则
        //更改正在质检状态
        log.info("开始质检文本内容对应的命中规则");
        List<Converse> converses = convertToList(jsonData, info);
        //文件内容信息处理
        List<Converse> converseList = converseRepository.saveAll(converses);
        if (haveSentence(info.getTaskInfo())) { //规则都是句子
            info.setRunDate(new Date());
            info = inspectionDetailRepository.save(info);
            try {
                executeSentence(info, converses);
            } catch (Exception e) {
                log.info("句子质检出错了！");
                e.printStackTrace();
            }
        } else {  //针对关键词质检
            //针对每句话做质检并保存
            List<ConverseRule> converseRuleList = saveConverseRule(info, converseList, ruleInfos);
            if (!ListUtils.isEmpty(converseRuleList)) {
                converseRuleRepository.saveAll(converseRuleList);
            }
            if (ruleInfos != null && !ruleInfos.isEmpty()) {
                info.setHitStatus(HitStatus.HIT);
                List<String> ruleName = ruleInfos.stream().map(e -> e.getRuleName()).collect(Collectors.toList());
                info.setHitRule(String.join(",", ruleName));//命中规则名称
            }
            //质检结果明细信息处理
            info.setRunDate(new Date());
            info.setQualityStatus(QualityStatus.FINISH);
            info = inspectionDetailRepository.save(info);
            log.info("质检完成，并修改质检明细对应的状态");
            //质检结果信息处理
            InspectionInfo inspectionInfo = saveInspectionInfo(info, ruleInfos);
            log.info("更新质检结果信息");
            inspectionRepository.save(inspectionInfo);
        }
    }

    /**
     * 更新质检结果
     *
     * @param info
     * @param ruleInfos
     */
    public InspectionInfo saveInspectionInfo(InspectionDetail info, Set<RuleInfo> ruleInfos) {
        InspectionInfo inspectionInfo = info.getInspectionInfo();
        //保存文件中命中的所有规则
        inspectionInfo.setRuleInfoSet(ruleInfos);
        if (info.getHitStatus().equals(HitStatus.HIT)) {
            inspectionInfo.setHitNum(inspectionInfo.getHitNum() + 1);
        }
//        Set<RuleInfo> ruleInfoSet = calculationResultService.getRuleInfosFromTask(inspectionInfo.getTaskInfo().getId());
        if (inspectionInfo.getVoiceNum() == 0) {
            inspectionInfo.setHitScale(new BigDecimal(0));
        } else {
            BigDecimal count = new BigDecimal(inspectionInfo.getVoiceNum());
            BigDecimal hitNumber = new BigDecimal(inspectionInfo.getHitNum());
            BigDecimal hitScale = hitNumber.divide(count, 4, BigDecimal.ROUND_HALF_UP);
            inspectionInfo.setHitScale(hitScale);
        }
        info.setQualityStatus(QualityStatus.FINISH);
        return inspectionInfo;
    }


    /**
     * 获取文本内容并命中规则信息
     *
     * @param jsonData 调用文件解析后的json
     * @param info
     * @return
     */
    public List<Converse> convertToList(String jsonData, InspectionDetail info) {
        //质检并解析语音识别语句
        List<Converse> converseList = new ArrayList<>();
        List<JsonDataModel> converses = JSONObject.parseArray(jsonData, JsonDataModel.class);
        if (CollectionUtil.isEmpty(converses))
            return converseList;
        for (JsonDataModel model : converses) {
            Converse converse = new Converse();
            converse.setFileName("");
            converse.setFilePath("");
            converse.setTaskInfo(info.getTaskInfo());
            converse.setInspectionDetail(info);
            converse.setBg(Long.valueOf(model.getBg()));
            converse.setEd(Long.valueOf(model.getEd()));
            converse.setStartTime(new Date());
            converse.setEndTime(new Date());
            converse.setSortNum(converses.indexOf(model));
            converse.setContent(model.getOnebest());
            converse.setSpeaker(model.getSpeaker());
            //静默时长
            if (converse.getSortNum() != 0) {
                long silent = DateUtil.compare(DateUtil.longToLocalDateTime(converse.getBg()), DateUtil.longToLocalDateTime(converseList.get(converseList.size() - 1).getEd()));
                converse.setSilentTime(silent);
            }
            //平均语速
            long length = DateUtil.compare(DateUtil.longToLocalDateTime(converse.getEd()), DateUtil.longToLocalDateTime(converse.getBg()));
            if (length != 0) {
                converse.setAvgSpeed(converse.getContent().length() / length);
            }
            converseList.add(converse);
        }
        return converseList;
    }


    public List<ConverseRule> saveConverseRule(InspectionDetail info, List<Converse> converseList, Set<RuleInfo> ruleInfos) {
        List<ConverseRule> resultList = new ArrayList<>();
        for (Converse converse : converseList) {
            List<ConverseRule> converseRuleList = new ArrayList<>();
            Set<RuleInfo> ruleInfoSet = calculationResultService.getCalculationRuleInfo(info.getTaskInfo().getId(), converse.getContent());
            if (!SetUtils.isEmpty(ruleInfoSet)) {
                ruleInfos.addAll(ruleInfoSet);//统计使用
                ruleInfoSet.forEach(ruleInfo -> {
                    ConverseRule converseRule = new ConverseRule();
                    log.info("converse的id是：" + converse.getId());
                    converseRule.setConverseId(converse.getId());
                    converseRule.setRuleInfo(ruleInfo);
                    resultList.add(converseRule);
                    converseRuleList.add(converseRule);
                });
            }
        }
        return resultList;
    }

    /**
     * 更新任务执行状态
     *
     * @param request
     * @param response
     */
    public void updateRunStatus(CqTaskRequest request, ResultResponse response) {
        Optional<TaskInfo> optional = taskInfoRepository.findById(Long.valueOf(request.getId()));
        if (!optional.isPresent()) {
            response.setStatus(500);
            response.setMgs("修改状态更新失败");
            throw new BadRequestException("", "修改的任务信息不存在！");
        }
        TaskInfo taskInfo = optional.get();
        if (request.getStatus().equals("excuting")) {
            taskInfo.setRunStatus(TaskStatus.EXCUTING);
        } else if (request.getStatus().equals("finshed")) {
            taskInfo.setRunStatus(TaskStatus.FINISH);
            //更新质检结果中的任务状态
            Sort sort = new Sort(Sort.Direction.DESC, "createTime");
            Iterator<InspectionInfo> iterator = inspectionRepository.findAll(sort).iterator();
            List<InspectionInfo> inspectionInfos = Lists.newArrayList(iterator);
            if (inspectionInfos != null && !inspectionInfos.isEmpty()) {
                InspectionInfo info = inspectionInfos.get(0);//获取最新的执行的结果
                info.setTaskStatus(InspTask.FINISH);
                inspectionRepository.save(info);
            }
        }
        taskInfoRepository.save(taskInfo);
    }


    /**
     * 对句子进行预测发送请求
     *
     * @param info
     * @param converses
     * @throws Exception
     */
    public void executeSentence(InspectionDetail info, List<Converse> converses) throws Exception {
        log.info("开始组装句子类型质检数据......");
        ScenesInfo scenesInfo = scenesRepository.findById(info.getTaskInfo().getScenesInfoId()).orElseThrow(() -> new BadRequestException("", "该场景不存在"));
        List<List<Object>> list = templateInfoService.getDrillHead(scenesInfo.getId());
        if (list != null && !list.isEmpty()) {
            int index = list.get(0).size();
            for (Converse converse : converses) {
                List<Object> textList = new ArrayList<>();
                for (int i = 0; i < (index - 1); i++) {
                    textList.add(0);
                }
                textList.add(converse.getContent());
                list.add(textList);
            }
            Drill drill = new Drill();
            drill.setData(list);
            drill.setId(scenesInfo.getId().toString());
            drill.setCode(scenesInfo.getScenesCode());
            drill.setName(scenesInfo.getName());
            drill.setCommand("predict");
            drill.setStatus("ready");
            drill.setDetailId(info.getId().toString());
            String json = JSONObject.toJSONString(drill);
            log.info("场景样本预测数据:" + json);
            SocketClientUtils.sendText(drillUrl, json);
        }
    }


    /**
     * 主要是针对句子类型的质检点进行处理
     *
     * @param request
     * @param response
     */
    @Transactional
    public void qcDetail(Drill request, ResultResponse response) {
        Long detailId = Long.valueOf(request.getDetailId());
        Optional<InspectionDetail> optional = inspectionDetailRepository.findById(detailId);
        if (!optional.isPresent()) {
            response.setStatus(500);
            response.setMgs("id:" + request.getDetailId() + "找不到对应的信息!");
            throw new BadRequestException("", "没有找到对应的质检明细信息！");
        }
        InspectionDetail inspectionDetail = optional.get();
        //获取该任务下的所有规则
        Set<ConverseRule> converseRuleSet = new HashSet<>();//获取更新规则后所有的文本信息命中的规则
        Set<RuleInfo> recordset = new HashSet<>();//记录文本信息中的所有命中规则
        //保证使用的时候必要对应的值
        if (request.getData() != null && request.getData().size() > 2) {
            executeRuleInfo(request, inspectionDetail, converseRuleSet, recordset);
        }
        if (!converseRuleSet.isEmpty()) {
            converseRuleRepository.saveAll(converseRuleSet);
        }
        if (recordset != null && !recordset.isEmpty()) {
            inspectionDetail.setHitStatus(HitStatus.HIT);
            List<String> ruleName = recordset.stream().map(e -> e.getRuleName()).collect(Collectors.toList());
            inspectionDetail.setHitRule(String.join(",", ruleName));//命中规则名称
        }
        inspectionDetail.setQualityStatus(QualityStatus.FINISH);
        inspectionDetailRepository.save(inspectionDetail);
    }

    /**
     * 对句子样本进行预测
     *
     * @param request
     * @param inspectionDetail
     * @param converseRuleSet
     * @param recordset
     */
    public void executeRuleInfo(Drill request, InspectionDetail inspectionDetail,
                                Set<ConverseRule> converseRuleSet, Set<RuleInfo> recordset) {
        Iterator<Converse> iterator = converseRepository.findAll(QConverse.converse.inspectionDetail.id.eq(inspectionDetail.getId())).iterator();
        List<Converse> converses = Lists.newArrayList(iterator);
        //获取该任务下的所有规则
        Set<RuleInfo> ruleInfos = inspectionDetail.getTaskInfo().getRuleInfoSet();

        //获取场景判断是否命中的标准
        Optional<ScenesInfo> optional = scenesRepository.findById(Long.valueOf(request.getId()));
        if (!optional.isPresent()) {
            throw new BadRequestException("", "id为:" + request.getId() + "的场景信息没有找到！");
        }
        ScenesInfo scenesInfo = optional.get();
        Double criticalValues = scenesInfo.getCriticalValues().doubleValue();
        for (int index = 2; index < request.getData().size(); index++) {
            List<Object> list = request.getData().get(index);
            List<String> formulaIds = new ArrayList<>();
            for (int i = 0; i < list.size() - 1; i++) {
                String value = (String) list.get(i);
                log.info("实际值是：" + value + ",标准值是：" + criticalValues);
                Double practical = Double.parseDouble(value);
                if (practical > criticalValues) {
                    String formulaId = request.getData().get(1).get(i).toString();
                    formulaIds.add(formulaId);
                }
            }
            Set<RuleInfo> ruleInfoSet = judgeRuleInfo(ruleInfos, formulaIds);
            if (SetUtils.isEmpty(ruleInfoSet)) {
                recordset.addAll(ruleInfoSet);
                InspectionInfo inspectionInfo = inspectionDetail.getInspectionInfo();
                inspectionInfo.setHitNum(inspectionInfo.getHitNum() + 1);
                if (inspectionInfo.getVoiceNum() == 0) {
                    inspectionInfo.setHitScale(new BigDecimal(0));
                } else {
                    BigDecimal count = new BigDecimal(inspectionInfo.getVoiceNum());
                    BigDecimal hitNumber = new BigDecimal(inspectionInfo.getHitNum());
                    BigDecimal hitScale = hitNumber.divide(count, 4, BigDecimal.ROUND_HALF_UP);
                    inspectionInfo.setHitScale(hitScale);
                }
                inspectionRepository.save(inspectionInfo);
            }
            String text = (String) list.get(list.size() - 1);
            log.info("质检的句子：" + text);
            List<Converse> newConverse = converses.stream().filter(converse -> converse.getContent().equals(text)).collect(Collectors.toList());
            for (Converse converse : newConverse) {
                Set<RuleInfo> ruleInfoByConverseId = new HashSet<>();
                converse.getRuleInfoSet().forEach(converseRule -> ruleInfoByConverseId.add(converseRule.getRuleInfo()));
                if (ruleInfoByConverseId != null && !ruleInfoByConverseId.isEmpty()) {
                    recordset.addAll(ruleInfoByConverseId);
                    ruleInfoSet.removeAll(ruleInfoByConverseId);
                }
                ruleInfoSet.forEach(ruleInfo -> {
                    ConverseRule converseRule = new ConverseRule();
                    converseRule.setConverseId(converse.getId());
                    converseRule.setRuleInfo(ruleInfo);
                    converseRuleSet.add(converseRule);
                });
            }
        }
    }

    /**
     * 句子质检命中规则处理逻辑
     *
     * @param ruleInfos
     * @param formulaIds
     * @return
     */
    public Set<RuleInfo> judgeRuleInfo(Set<RuleInfo> ruleInfos, List<String> formulaIds) {
        Set<RuleInfo> resultSet = new HashSet<>();
        if (ruleInfos != null && !ruleInfos.isEmpty()) {
            ruleInfos.forEach(ruleInfo -> {
                String relation = ruleInfo.getRelation();
                List<String> formulaCodes = Arrays.asList(relation.split("\\||&|\\(|\\)"));
                Map<String, Integer> resultMap = new HashMap();
                for (String code : formulaCodes) {
                    FormulaInfo formula = formulaInfoRepository.findOne(QFormulaInfo.formulaInfo.formulaCode.eq(code.trim())).orElseThrow(() -> new BadRequestException("", "根据质检点编码没有查到质检点"));
                    relation = relation.replace(code, formula.getId().toString());
                    if (formulaIds.contains(formula.getId().toString())) {
                        resultMap.put(formula.getId().toString(), 1);
                    } else {
                        resultMap.put(formula.getId().toString(), 0);
                    }
                }
                if (FormulaUtil.jsonToFormula(relation, resultMap) == 1) resultSet.add(ruleInfo);
            });
        }
        return resultSet;
    }


    /**
     * 判断进行更新任务状态
     *
     * @param inspectionInfo
     */
    @Transactional
    public void updateTaskInfoByInspectionDetail(InspectionInfo inspectionInfo) {
        //抽检数量和已经完成的数量是否一致
        if (inspectionInfo.getVoiceNum().equals(inspectionInfo.getComplateNum())) {
            //开始更新任务状态
            inspectionInfo.setTaskStatus(InspTask.FINISH);
            inspectionRepository.save(inspectionInfo);
            TaskInfo taskInfo = inspectionInfo.getTaskInfo();
            taskInfo.setRunStatus(TaskStatus.FINISH);
            taskInfo.setEndTime(new Date());
            taskInfoRepository.save(taskInfo);
        }
    }

    /**
     * 根据任务查询此任务下是否有包含句子类型的质检点
     *
     * @param taskInfo 任务信息
     * @return
     */
    public boolean haveSentence(TaskInfo taskInfo) {
        Set<RuleInfo> ruleInfoSet = taskInfo.getRuleInfoSet();
        if (!SetUtils.isEmpty(ruleInfoSet)) {
            for (RuleInfo ruleInfo : ruleInfoSet) {
                Set<FormulaInfo> formulaInfos = ruleInfo.getFormulaInfos();
                if (!SetUtils.isEmpty(formulaInfos)) {
                    for (FormulaInfo formulaInfo : formulaInfos) {
                        if (formulaInfo.getFormulaType().equals(FormulaType.SENTENCE)) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
}
